TechNote - Hyperlinks
Thursday, June 11, 2026
8:22 AM
6/12/2026 6:46 AM OneNote lets users create hyperlinks to notebooks, sections, pages, and paragraphs, all with a simple right-click. These hyperlinks can be pasted into any OneNote page as a way of creating relationships and mind maps between pages across notebooks and sections.
But those hyperlinks are opaque - there is no publicly available documentation that explains how OneNote generates hyperlinks. Unfortunately, the IDs used in hyperlinks have no obvious correlation to the IDs provided by the OneNote COM API through GetHieararchy or GetPageContent, making it impossible to reverse-engineer or map a hyperlink to the well-known XML metadata normally used. And worse, those URIs differ from the URIs returned by the GetHyperlinkToObject API.
Two ID Namespaces
The OneNote COM API maintains two distinct identifier spaces that don't have a documented transformation between them.
Hierarchy/page XML IDs look like this:
{5A643683-78AF-00B9-2CF8-5870571CB377}{1}{E194715543909058...}
A GUID followed by {1} and a long numeric suffix.
While hyperlink URI IDs from GetHyperlinkToObject look like:
section-id={02FE7AAF-7BC9-4F97-A7B2-B46ECEBB2BBA}
page-id={8857B2AA-4D63-4A8F-84C9-70A07D0F75EB}
object-id={DB6EF273-8B33-09B3-127A-7339FE620CE6}
Plain GUIDs in braces, no suffix. The IDs are in the same general GUID format but the suffixes present in the XML hierarchy IDs ({1}{B0}, long numeric strings) are stripped in the hyperlink representation. Microsoft has never documented the relationship between these two forms.
Translation Techniques
Forward Map (Heavy)
OneMore has been using a forward map for a very long time. In this approach, we build a map by calling GetHyperlinkToObject on every object we care about within a defined scope and storing the association in an ephemeral cache.
- Enumerate the hierarchy via GetHierarchy to get all section/page XML IDs
- For each page, call GetPageContent to get object/OE IDs
- Call GetHyperlinkToObject(pageId, objectId, ...) for each
- Store the result in a dictionary to map {hyperlink-URI-GUID} → {xml-id}
This approach is generally reliable (if you can keep the COM connection stable for the lifetime of the probe phase). But it is also extremely expensive, long enough that users notice and have to wait while watching a progress bar slowly increment.
Navigation Cheat (Disruptive)
Given a hyperlink URI, you could use that to NavigateTo that target, read the relevant IDs, and then navigate back to the original location. This is slow, visually surprising, and not always practical when working with more than one URI.
Targeted Lookup (Best, but with caveats!)
Hyperlink URIs have two forms - local and remote - and are generally consistent in their structure.
When the user right-clicks a paragraph in a notebook hosted in OneDrive and choses Copy Link to Paragraph, the clipboard will contain a Text entry with two lines that looks something like (modified)
https: //onedrive.live.com/view.aspx?resid=<private>%2158599&id=documents&wd=target%28Demo%20Section%20Group%2FDemo%20Section.one%7CA31F01F5-B99C-4F24-B6BF-D95D3A71EABF%2FDemo%20Page%7C9A94BD92-C7A1-418D-ABD9-20FB70B697AA%2F%29&wdpartid={ED9D3A77-B166-45C4-88D6-B8E840BF3152}{1}&wdsectionfileid=6925D0374517D4B4!58610&end
onenote: https: //d.docs.live.net/<private>/Documents/OneMore%20Smoke%20Tests%20(Read-Only)/Demo%20Section%20Group/Demo%20Section.one#Demo%20Page§ion-id={A31F01F5-B99C-4F24-B6BF-D95D3A71EABF}&page-id={9A94BD92-C7A1-418D-ABD9-20FB70B697AA}&end
The second line is interesting because it contains the notebook name, section group path, section .one file name, and page name. It may even contains an &object-id parameter, although remember none of these IDs can be correlated to XML IDs.
Right-clicking a paragraph in a locally hosted notebook would like something like:
onenote: ///\\COHN-SPECTRE\Userssteve\Documents\Local%20Notebooks\Local\open.one#Things%20are%20Great§ion-id={E0C21B1E-390C-40BE-8E14-DD84D34ADDDC}&page-id={7FE33A96-E03E-44AE-9159-C84CBBD43C22}&object-id={E79DB3B0-BCEB-0671-3E8E-900E589FE443}&F3
We can parse this into its individual parts and use that information.
- COHN-SPECTRE is the local machine name, $env:COMPUTERNAME
- Userssteve is a combination of Users\steve, pointing to the local $env:USERPROFILE path C:\Users\steve
- "Local Notebooks" is a folder path in my user profile
- "Local" is the notebook name
- "open" is the section name
- "Things are Great" is the page name
The tricky part is that the folder path may be an arbitrary depth. So an parsing algorithm needs to incrementally build a path to the .one file to confirm that the file can be found and, if not, rethink its understanding of what is "path" is what is "notebook/section-group/section" within the URI.
Because the path prior to the notebook name may follow unknown patterns, it may be helpful to let the user specify one or more search paths in Settings as a fallback in case the .one file cannot be found simply by parsing. (TBD)
OneNoteLinkParser
This is what's implemented by the OneMore\Helpers\OneNoteLinkParser class. This is used to resolve hyperlink URIs to path names - notebook/section/page.
If the URI stops at page-id then we have the page and can easily determine its page ID. This is the ideal case with no forward lookup needed at all.
If the URI includes an object-id then we can open the page, and create a breaking forward lookup of objects on the page - iterate each and stop when we get to one that matches the URI. Forward lookup is scoped to a single page.
But…! What does this mean in practicality?
Do not rely on Copy Link to Paragraph - that is a dead end.
Copy Link to Paragraph will return a hyperlink URI as you would expect. For every paragraph on the page, the URIs include a virtual section-id, page-id, and object-id and only differ by a trailing &XX link terminator, such as the &2F terminator below.
Link created by Copy Link to Paragraph:
onenote:///\\COHN-SPECTRE\Userssteve\Documents\Local%20Notebooks\Local\open.one#second§ion-id={E0C21B1E-390C-40BE-8E14-DD84D34ADDDC}&page-id={504EDBD0-A7DC-4911-90AA-207C82E80ED2}&object-id={C7A61492-E478-05B5-24DE-500863B4266E}&2F
There is no way to translate hyperlink URIs to XML ssection IDs, page IDs and paragraph objectIds. So we have to try getting the hyperlink URI of each using GetHyperlinkToObject and then compare with the selected paragraph's URI.
Link created by GetHyperlinkToObject - for the exact same paragraph:
onenote:///\\COHN-SPECTRE\Userssteve\Documents\Local%20Notebooks\Local\open.one#second§ion-id={E0C21B1E-390C-40BE-8E14-DD84D34ADDDC}&page-id={504EDBD0-A7DC-4911-90AA-207C82E80ED2}&object-id={C7A61492-E478-05B5-24DE-500863B4266E}&30
The hyperlinks almost match - the base address, notebook name, section-id, page-id, object-id are all equivalent, except the terminator is always different (2F does not equal 30!) And there's still no way to correlate URIs from GetHyperlinkToObject with those from Copy Link to Paragraph. Even though both full URIs will navigate you to the same paragraph!
The object-id value references the Outline, not any specific OE. It's unclear what the link terminator actually does. Some say it's an offset - but from where? Some think that offset would change if you add or remove content in the Outline. but that's just not the case. So, there's some witchcraft going on behind the scenes, and Microsoft ain't talking.
Full disclosure, we could use the URI to navigate to the paragraph, grab the ID, and then navigate back. But that would create a very disruptive visual experience for the user. "But I can live with that!", you might say. No. No, you can't. And I won't allow it. 'nuf said.
End of story.
So for behaviors such as the Embed command, we can use Copy Link to Page to initiate a copy and then ask for start/end delimiters. Honestly, Copy Link to Paragraph has the same info, so that would work too if we just ignore the object-id part.
So, we're stuck with always using delimiters - and never rely on the ID values in hyperlink URIs to be unique or idempotent.
#omwiki #omdeveloper #omtechnote
© 2020 Steven M Cohn. All rights reserved.
Please consider a sponsorship or one-time donation to support ongoing development
Created with OneNote.